package com.zhx.common.chat.service.strategy.msg;

import cn.hutool.core.collection.CollectionUtil;
import com.zhx.common.chat.dao.MessageDao;
import com.zhx.common.chat.domain.entity.Message;
import com.zhx.common.chat.domain.entity.msg.MessageExtra;
import com.zhx.common.chat.domain.enums.MessageStatusEnum;
import com.zhx.common.chat.domain.enums.MessageTypeEnum;
import com.zhx.common.chat.domain.vo.request.msg.TextMsgReq;
import com.zhx.common.chat.domain.vo.response.msg.TextMsgResp;
import com.zhx.common.chat.service.adapter.MessageAdapter;
import com.zhx.common.chat.service.cache.MsgCache;
import com.zhx.common.common.domain.enums.YesOrNoEnum;
import com.zhx.common.common.util.AssertUtil;
import com.zhx.common.common.util.discover.PrioritizedUrlDiscover;
import com.zhx.common.common.util.discover.doamin.UrlInfo;
import com.zhx.common.user.domain.entity.User;
import com.zhx.common.user.domain.enums.RoleEnum;
import com.zhx.common.user.service.IRoleService;
import com.zhx.common.user.service.cache.UserCache;
import com.zhx.common.user.service.cache.UserInfoCache;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 普通文本消息
 */
@Component
public class TextMsgHandler extends AbstractMsgHandler<TextMsgReq>{

    @Resource
    private MessageDao messageDao;

    @Resource
    private UserInfoCache userInfoCache;

    @Resource
    private IRoleService iRoleService;

    @Resource
    private MsgCache msgCache;

    @Resource
    private UserCache userCache;

    private static final PrioritizedUrlDiscover URL_TITLE_DISCOVER = new PrioritizedUrlDiscover();

    @Override
    MessageTypeEnum getMsgTypeEnum() {
        return MessageTypeEnum.TEXT;
    }

    @Override
    protected void checkMsg(TextMsgReq body, Long roomId, Long uid) {
        //校验下回复消息
        if(Objects.nonNull(body.getReplyMsgId())){
            Message replyMsg = messageDao.getById(body.getReplyMsgId());
            AssertUtil.isNotEmpty(replyMsg,"回复消息不存在");
            AssertUtil.equal(replyMsg.getRoomId(),roomId,"只能回复相同会话内的消息");
        }

        //校验@的人
        if (CollectionUtil.isNotEmpty(body.getAtUidList())) {
            //前端传入的@用户列表可能会重复，需要去重
            List<Long> atUidList = body.getAtUidList().stream().distinct().collect(Collectors.toList());
            Map<Long, User> batch = userInfoCache.getBatch(atUidList);
            //如果@用户不存在，userInfoCache 返回的map中依然存在该key，但是value为null，需要过滤掉再校验
            long batchCount = batch.values().stream().filter(Objects::nonNull).count();
            AssertUtil.equal((long)atUidList.size(), batchCount, "@用户不存在");
            boolean atAll = body.getAtUidList().contains(0L);
            if (atAll) {
                AssertUtil.isTrue(iRoleService.hasPower(uid, RoleEnum.CHAT_MANAGE), "没有权限");
            }
        }
    }

    /**
     * 保存消息的方法，将消息内容保存到数据库中。
     * @param message 包含消息基本信息的对象，如消息ID和额外信息。
     * @param body 包含消息体的内容，可能还包括回复消息的ID和被@用户的列表。
     */
    @Override
    protected void saveMsg(Message message, TextMsgReq body) {
        // 如果message的extra为空，则新建一个MessageExtra对象
        MessageExtra extra = Optional.ofNullable(message.getExtra()).orElse(new MessageExtra());
        Message update = new Message();
        update.setId(message.getId());
        // todo：对body的内容进行敏感词过滤
        update.setContent(body.getContent());
        update.setExtra(extra);

        // 如果是回复消息，计算消息间隔并设置回复消息ID
        if(Objects.nonNull(body.getReplyMsgId())){
            Integer gapCount = messageDao.getGapCount(message.getRoomId(),body.getReplyMsgId(),message.getId());
            update.setGapCount(gapCount);
            update.setReplyMsgId(body.getReplyMsgId());
        }

        //处理消息URL跳转的逻辑
        Map<String, UrlInfo> urlContentMap = URL_TITLE_DISCOVER.getUrlContentMap(body.getContent());
        extra.setUrlContentMap(urlContentMap);

        // 如果body中包含被@的用户列表，更新extra中的atUidList
        if(CollectionUtil.isNotEmpty(body.getAtUidList())){
            extra.setAtUidList(body.getAtUidList());
        }

        // 更新消息信息到数据库
        messageDao.updateById(update);
    }

    /**
     * 显示消息的处理方法。
     *
     * @param msg 输入的消息对象，包含消息内容、额外信息（如URL内容和@用户列表）以及可能的回复消息ID。
     * @return 返回一个包含消息内容、额外信息、可能的回复消息信息的对象。
     */
    @Override
    public Object showMsg(Message msg) {
        TextMsgResp resp = new TextMsgResp();
        // 设置消息内容
        resp.setContent(msg.getContent());
        // 设置额外信息中的URL内容和@用户列表
        resp.setUrlContentMap(Optional.ofNullable(msg.getExtra()).map(MessageExtra::getUrlContentMap).orElse(null));
        resp.setAtUidList(Optional.ofNullable(msg.getExtra()).map(MessageExtra::getAtUidList).orElse(null));

        // 检查并设置回复消息
        Optional<Message> reply = Optional.ofNullable(msg.getReplyMsgId())
                .map(msgCache::getMsg)
                .filter(a -> Objects.equals(a.getStatus(), MessageStatusEnum.NORMAL.getStatus()));
        if (reply.isPresent()) {
            // 处理并设置回复消息的详细信息
            Message replyMessage = reply.get();
            TextMsgResp.ReplyMsg replyMsgVO = new TextMsgResp.ReplyMsg();
            replyMsgVO.setId(replyMessage.getId());
            replyMsgVO.setUid(replyMessage.getFromUid());
            replyMsgVO.setType(replyMessage.getType());

            // 使用消息处理器展示回复消息的内容
            replyMsgVO.setBody(MsgHandlerFactory.getStrategyNoNull(replyMessage.getType()).showReplyMsg(replyMessage));

            // 设置回复消息发送者的用户名
            User replyUser = userCache.getUserInfo(replyMessage.getFromUid());
            replyMsgVO.setUsername(replyUser.getName());

            // 设置是否可以进行回调的标志
            replyMsgVO.setCanCallback(YesOrNoEnum.toStatus(Objects.nonNull(msg.getGapCount()) && msg.getGapCount() <= MessageAdapter.CAN_CALLBACK_GAP_COUNT));
            replyMsgVO.setGapCount(msg.getGapCount());

            resp.setReply(replyMsgVO);
        }
        return resp;
    }

    @Override
    public Object showReplyMsg(Message msg) {
        return msg.getContent();
    }

    @Override
    public String showContactMsg(Message msg) {
        return msg.getContent();
    }
}
